/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */
#include "PlayerSessionManager.h"
#include "ServiceManager.h"
#include "Logger.h"
#include "PQException.h"

#define ACCEPT_ONESTEP 50

namespace PQServiceLogin
{
	/*player session pool
	*/
	class PlayerSessionPool
	{
	};

	PlayerSessionManager::PlayerSessionManager(void)
	{
		//init network data field
		FD_ZERO(&_fdRead[SELECT_BAK]);
		FD_ZERO(&_fdWrite[SELECT_BAK]);
		_timeout[SELECT_BAK].tv_sec=0;
		_timeout[SELECT_BAK].tv_usec=0;
		_fdMax=_fdMin=INVALID_SOCKET;
		_sessionCount=0;

		for(int i=0;i<MAX_PLAYER;++i){
			_playerSessions[i]=0;
		}

		//init();
	}

	void PlayerSessionManager::init()
	{
		//init network
		int port=ServiceManager::getSingleton().getServicePort();

		_listenSocket=new PQNet::TCPSocket();

		_listenSocket->open();
		_listenSocket->setReuseAddr();

		_listenSocket->bind(port);
		_listenSocket->listen();

		_listenSocket->setNonBlocking();

		_fdMax=_fdMin=_listenSocket->getSocketHandle();

		//init player
		_sessionCount = 0;
		for( int i=0; i<MAX_PLAYER; ++i){
			_playerSessions[i]=0;
		}

		/*init packet queue;
		*/
		PQLogger.log("PlayerSessionManager init...");
	}


	PlayerSessionManager::~PlayerSessionManager(void)
	{
	}


	bool PlayerSessionManager::addSession(PlayerSession* session)
	{
		if(_sessionCount>=FD_SETSIZE){
			return false;//overflow
		}

		SOCKET fd=session->getSocketHandle();
		_fdMin=min(fd,_fdMin);
		_fdMax=max(fd,_fdMax);

		FD_SET(fd , &_fdRead[SELECT_BAK]);
		FD_SET(fd , &_fdWrite[SELECT_BAK]);
		FD_SET(fd , &_fdExcept[SELECT_BAK]);

		_sessionCount++;

		return true;
	}

	void PlayerSessionManager::removeSession(PlayerSession* session)
	{
		PQThrowNotImpl();

	}

	void PlayerSessionManager::select()
	{
		_timeout[SELECT_USE].tv_sec=_timeout[SELECT_BAK].tv_sec;
		_timeout[SELECT_USE].tv_usec=_timeout[SELECT_BAK].tv_usec;

		_fdRead[SELECT_USE]=_fdRead[SELECT_BAK];
		_fdWrite[SELECT_USE]=_fdWrite[SELECT_BAK];
		_fdExcept[SELECT_USE]=_fdExcept[SELECT_BAK];


		int result = ::select(_fdMax+1 , 
			&_fdRead[SELECT_USE],
			&_fdWrite[SELECT_USE],
			&_fdExcept[SELECT_USE],
			&_timeout[SELECT_USE]);

		PQAssert(result != SOCKET_ERROR);

	}

	void PlayerSessionManager::update()
	{
		//network select
		select();
		//process connection exceptions
		processExceptions();
		
		processInput();
		processOutput();
		processCommand();

	}

	void	PlayerSessionManager::processExceptions()
	{
		//process player network disconnect.
		for(int i=0;i<_sessionCount;++i){
			
			PlayerSession* session=_playerSessions[i];
			if(!session){
				continue;
			}

			SOCKET s=session->getSocketHandle();
			if(s==_listenSocket->getSocketHandle()){
				PQAssert("Socket handle error.");
				continue;
			}

			if(FD_ISSET(s,&_fdExcept[SELECT_USE])){
				removeSession(session);
			}
		}

	}

	bool PlayerSessionManager::processInput()
	{
		if(_fdMax==INVALID_SOCKET&&_fdMin==INVALID_SOCKET){
			return true;
		}

		//new connection set
		SOCKET s=_listenSocket->getSocketHandle();
		if(FD_ISSET(s,&_fdRead[SELECT_USE])){
			for(int i=0;i<ACCEPT_ONESTEP;++i){
				if(!acceptConnection()){
					break;//if no user connect
				}
			}
		}

		//read data
		for(int i=0;i<_sessionCount;++i){
			PlayerSession* session=_playerSessions[i];
			if(!session){
				continue;
			}

			SOCKET s = session->getSocketHandle();
			if(s==_listenSocket->getSocketHandle()){
				continue;
			}

			if(FD_ISSET(s, &_fdRead[SELECT_USE])){
				if(session->getSocket()->isSocketError()){
					//connection error
					removeSession(session);
				}else{
					//connection successed
					if(!session->processInput()){
						removeSession(session);
					}
				}
			}
		}
		return false;
	}

	bool PlayerSessionManager::processOutput()
	{
		if(_fdMin==INVALID_SOCKET&&_fdMax==INVALID_SOCKET){
			//no player exist
			return true;
		}

		for(int i=0;i<_sessionCount;++i){
			PlayerSession* session=_playerSessions[i];
			if(!session){
				continue;
			}
			SOCKET s=session->getSocketHandle();
			if(s==_listenSocket->getSocketHandle()){
				continue;
			}

			if(FD_ISSET(s,&_fdWrite[SELECT_USE])){
				if(session->getSocket()->isSocketError()){
					removeSession(session);
				}else{
					if(!session->processOutput()){
						removeSession(session);
					}
				}
			}
		}

		return true;
	}

	bool PlayerSessionManager::processCommand()
	{
		if(_fdMin==INVALID_SOCKET&&_fdMax==INVALID_SOCKET){
			//no player exist
			return true;
		}

		for(int i=0;i<_sessionCount;++i){
			PlayerSession* session=_playerSessions[i];
			if(!session){
				continue;
			}
			SOCKET s=session->getSocketHandle();
			if(s==_listenSocket->getSocketHandle()){
				continue;
			}

			if(FD_ISSET(s,&_fdWrite[SELECT_USE])){
				if(session->getSocket()->isSocketError()){
					removeSession(session);
				}else{
					if(!session->processCommand()){
						removeSession(session);
					}
				}
			}
		}

		return true;
	}

	bool PlayerSessionManager::acceptConnection()
	{
		SOCKADDR_IN sockAddr;
		memset(&sockAddr, 0, sizeof(SOCKADDR_IN));

		int addrlen=sizeof(SOCKADDR_IN);

		SOCKET s= _listenSocket->accept((struct sockaddr *)(&sockAddr), &addrlen);

		if(s == INVALID_SOCKET){
			
			return false ;
		}


		PlayerSession* session=new PlayerSession(s);
		addSession(session);

		return true;
	}


}
